package net.cyue.easyconfiguration.toml;

import net.cyue.easyconfiguration.api.IConfigurationResource;
import net.cyue.easyconfiguration.node.NodeType;
import net.cyue.util.ResourceUtil;
import net.cyue.util.StringUtil;
import org.tomlj.Toml;
import org.tomlj.TomlArray;
import org.tomlj.TomlParseResult;
import org.tomlj.TomlTable;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class TOMLConfigurationResource implements IConfigurationResource {

    private final TomlParseResult tomlResult;

    public TOMLConfigurationResource(String toml) throws IOException {
        this.tomlResult = Toml.parse(toml);
    }
    public TOMLConfigurationResource(InputStream inputStream) throws IOException {
        this.tomlResult = Toml.parse(inputStream);
    }

    public static TOMLConfigurationResource fromResource(String resourceName) throws IOException {
        return new TOMLConfigurationResource(ResourceUtil.getResourceAsStream(resourceName));
    }

    public Object[] array2Array(TomlArray tomlArray) {
        Object[] array = new Object[tomlArray.size()];
        for (int i = 0; i < tomlArray.size(); i++) {
            Object value = tomlArray.get(i);
            if (value instanceof TomlTable) {
                array[i] = this.table2Map((TomlTable) value);
            } else if (value instanceof TomlArray) {
                array[i] = this.array2Array((TomlArray) value);
            } else {
                array[i] = value;
            }
        }
        return array;
    }
    public Map<String, Object> table2Map(TomlTable table) {
        Map<String, Object> map = new HashMap<>();
        for (String key : table.keySet()) {
            Object value = table.get(key);
            if (value instanceof TomlTable) {
                map.put(key, this.table2Map((TomlTable) value));
            } else if (value instanceof TomlArray) {
                map.put(key, this.array2Array((TomlArray) value));
            } else {
                map.put(key, value);
            }
        }
        return map;
    }

    @Override
    public NodeType getNodeType(String path) {
        Object node = this.tomlResult.get(path);
        if (node == null) {
            return NodeType.ANY;
        } else if (node instanceof TomlTable) {
            return NodeType.TABLE;
        } else if (node instanceof TomlArray) {
            return NodeType.ARRAY;
        } else if (node instanceof String) {
            return NodeType.STRING;
        } else if (node instanceof Number) {
            return NodeType.NUMBER;
        } else if (node instanceof Boolean) {
            return NodeType.BOOLEAN;
        } else {
            return NodeType.fromClass(node.getClass());
        }
    }

    @Override
    public List<String> getTableKeyList(String tablePath) {
        if (StringUtil.isBlank(tablePath) || tablePath.trim().equals("*")) {
            return new ArrayList<>(this.tomlResult.keySet());
        }
        TomlTable table = this.tomlResult.getTableOrEmpty(tablePath);
        if (table == null) {
            return new ArrayList<>();
        }
        return new ArrayList<>(table.keySet());
    }

    @Override
    public Object get(String path) {
        Object value = this.tomlResult.get(path);
        if (value == null) {
            return null;
        } else if (value instanceof TomlTable) {
            return this.table2Map((TomlTable) value);
        } else if (value instanceof TomlArray) {
            return this.array2Array((TomlArray) value);
        } else {
            return value;
        }
    }

    @Override
    public String getString(String path) {
        return this.tomlResult.getString(path);
    }

    @Override
    public Number getNumber(String path) {
        return this.tomlResult.getDouble(path);
    }

    @Override
    public Boolean getBoolean(String path) {
        return this.tomlResult.getBoolean(path);
    }

    @Override
    public Object[] getArray(String path) {
        TomlArray tomlArray = this.tomlResult.getArray(path);
        if (tomlArray == null) {
            return new Object[0];
        }
        return this.array2Array(tomlArray);
    }

    @Override
    public Map<String, Object> getTable(String path) {
        Map<String, Object> map = new HashMap<>();
        TomlTable table = this.tomlResult.getTable(path);
        if (table == null) {
            return map;
        }
        return this.table2Map(table);
    }
}
